/**
 * @namespace Formatting and conversion utilities.
 */
Jelo.Format = function() {
    /** @scope Jelo.Format */
    return {
        /**
         * Converts a string from CSS hyphenated to Javascript camelCase.
         * 
         * <pre><code>
        // example:
        var property = Jelo.Format.toCamel("margin-left");
        alert(property); // alerts "marginLeft"
        </code></pre>
         */
        toCamel               : function(str) {
            return str.replace(/-(.)/g, function(m, l) {
                return l.toUpperCase();
            });
        },
        
        /**
         * @deprecated v1.02: Too long a name, replaced by
         * {@link Jelo.Format.toCamel}
         */
        hyphenatedToCamelCase : function(str) {
            return Jelo.Format.toCamel(str);
        },
        
        /**
         * Mainly used by internal Jelo functions. If a hash 
         * mark (#) appears at the beginning of the string, it 
         * will be stripped.
         * 
         * @param {String} hex A value such as "#0080FF"
         * @returns {String} A value such as "0080FF"
         */
        cutHexHash            : function(h) {
            return (h.charAt(0) == "#") ? h.substring(1) : h;
        },
        /**
         * Returns the "red" value from a CSS-style hex string.
         * 
         * @param {String} hex A value such as "#9966CC"
         * @returns {String} For the above example, "99"
         */
        hexToR                : function(h) {
            return parseInt(Jelo.Format.cutHexHash(h).substring(0, 2), 16);
        },
        /**
         * Returns the "green" value from a CSS-style hex string.
         * 
         * @param {String} hex A value such as "#9966CC"
         * @returns {String} For the above example, "66"
         */
        hexToG                : function(h) {
            return parseInt(Jelo.Format.cutHexHash(h).substring(2, 4), 16);
        },
        /**
         * Returns the "blue" value from a CSS-style hex string.
         * 
         * @param {String} hex A value such as "#9966CC"
         * @returns {String} For the above example, "CC"
         */
        hexToB                : function(h) {
            return parseInt(Jelo.Format.cutHexHash(h).substring(4, 6), 16);
        },
        /**
         * Splits a CSS-style hex value into a array of Red, Green, and Blue values.
         * 
         * @param {String} hex A value such as "#9966CC"
         * @returns {Array} For the above example, ["99", "66", "CC"]
         */
        hexToRGB              : function(h) {
            var r = this.hexToR(h);
            var g = this.hexToG(h);
            var b = this.hexToB(h);
            return [r, g, b];
        },
        
        /**
         * Converts a CSS RGB string to an array of RGB values. Mainly useful to
         * internal Jelo functions.
         * 
         * @param {String} hex A value such as "rgb(0, 128, 255)"
         * @returns {Array} For the above example, [0, 128, 255]
         */
        rgbStringToArray      : function(s) {
            if (s.isString) {
                try {
                    var sub = s.split(/\D/g);
                    var sub2 = [];
                    for (var i = 0; i < sub.length; i++) {
                        if (sub[i]) {
                            sub2[sub2.length] = parseInt(sub[i], 10);
                        }
                    }
                    return sub2;
                } catch (e) {
                    throw new Error("Jelo.Format.rgbStringToArray: Invalid input " + s);
                }
            } else {
                return [];
            }
        },
        
        /**
         * Converts a CSS RGB string to a CSS hex string. Mainly useful to
         * internal Jelo functions.
         * 
         * @param {String} hex A value such as "rgb(0, 128, 255)"
         * @returns {Array} For the above example, "#0080FF"
         */
        rgbToHex              : function(s) {
            if (s.isString) {
                try {
                    function toHex(n) {
                        var chr = "0123456789ABCDEF";
                        if (n === null) {
                            return "00";
                        }
                        n = parseInt(n, 10);
                        if (isNaN(n) || !n) {
                            return "00";
                        }
                        n = Math.max(0, n);
                        n = Math.min(n, 255);
                        n = Math.round(n);
                        return chr.charAt((n - n % 16) / 16) + chr.charAt(n % 16);
                    }
                    var a = Jelo.Format.rgbStringToArray(s);
                    return "#" + toHex(a[0]) + toHex(a[1]) + toHex(a[2]);
                } catch (e) {
                    throw new Error("Jelo.Format.rgbStringToHex: Invalid input " + s);
                }
            }
        },
        
        /**
         * Port of http://php.net/urldecode by http://kevin.vanzonneveld.net
         * 
         * @see <a href="http://php.net/urldecode">PHP documentation</a>
         * @see <a href="http://kevin.vanzonneveld.net">Original port</a>
         * @param {String} str
         * @returns {String}
         */
        urldecode             : function(str) {
            var /* histogram */h = {};
            var ret = str.toString();
            function rep(s, r, str) {
                var tmp_arr = [];
                tmp_arr = str.split(s);
                return tmp_arr.join(r);
            }
            h["'"] = '%27';
            h['('] = '%28';
            h[')'] = '%29';
            h['*'] = '%2A';
            h['~'] = '%7E';
            h['!'] = '%21';
            h['%20'] = '+';
            for (var r in h) {
                if (h.hasOwnProperty(s)) {
                    search = h[r]; // Switch order when decoding
                    ret = rep(s, r, ret); // Custom replace. No regexing   
                }
            }
            return decodeURIComponent(ret);
        },
        
        /**
         * Port of http://php.net/urlencode by http://kevin.vanzonneveld.net
         * 
         * @see <a href="http://php.net/urldecode">PHP documentation</a>
         * @see <a href="http://kevin.vanzonneveld.net">Original port</a>
         * @param {String} str
         * @returns {String}
         */
        urlencode             : function(str) {
            var /* histogram */h = {}, tmp_arr = [];
            var ret = str.toString();
            function rep(s, r, str) {
                var tmp_arr = [];
                tmp_arr = str.split(s);
                return tmp_arr.join(r);
            }
            h["'"] = '%27';
            h['('] = '%28';
            h[')'] = '%29';
            h['*'] = '%2A';
            h['~'] = '%7E';
            h['!'] = '%21';
            h['%20'] = '+';
            ret = encodeURIComponent(ret);
            for (var s in h) {
                if (h.hasOwnProperty(s)) {
                    r = h[s]; // Switch order when encoding
                    ret = rep(s, r, ret); // Custom replace. No regexing
                }
            }
            return ret.replace(/(\%([a-z0-9]{2}))/g, function(full, m1, m2) {
                return "%" + m2.toUpperCase();
            });
        },
        
        /**
         * Alias for {@link Jelo.Format.urldecode}
         */
        urlDecode             : function(str) {
            return Jelo.Format.urldecode(str);
        },
        
        /**
         * Alias for {@link Jelo.Format.urlencode}
         */
        urlEncode             : function(str) {
            return Jelo.Format.urlencode(str);
        },
        
        /**
         * Converts a valid JSON string to a native Object.
         * 
         * @param {String} json A JSON-encoded string, e.g. '{ name : "John", age: "28" }'
         * @returns {Object} The object represented by valid input, or {} for invalid input.
         */
        fromJSON              : function(str) {
            try {
                var crockford = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/;
                var replace = /"(\\.|[^"\\])*"/g;
                return !(crockford.test(o.replace(replace, ''))) && eval('(' + o + ')');
            } catch (e) {
                return {};
            }
        }
    };
}();
